package algebra.rewriting.relational;

import java.util.HashSet;
import java.util.Set;

import kb.DescriptionLogicsKB;
import reasoner.KBReason;
import utils.exception.CoreReasoningException;
import entities.concept.AndConcept;
import entities.concept.Concept;
import algebra.cost.PlanEnumeration;
import algebra.op.AlgebraicOP;
import algebra.op.ConstantOP;
import algebra.op.JoinOP;
//import algebra.op.ProjectionOP;
import algebra.op.ScanOP;
import algebra.op.SelectionOP;
import algebra.rewriting.RewriteRule;

/**
 * Given a projection query, check if it can be simplified
 * 
 * @author j55wu
 * 
 */
public class RemoveSelection extends RewriteRule {
	private final AlgebraicOP op;
	private final DescriptionLogicsKB kb;

	public RemoveSelection(AlgebraicOP op, PlanEnumeration pe) {
		this.op = op;
		this.kb = pe.getKB();
	}

	@Override
	public AlgebraicOP rewrite() {
		// AlgebraicOP ret = null;
		SelectionOP so = op.asSelectionOP();
		Concept selc = so.getSelectionCondition();
		KBReason res = new KBReason();
		Set<Concept> as = new HashSet<Concept>();
		if (so.getSubquery() instanceof JoinOP) {
			JoinOP ip = (JoinOP) so.getSubquery();
			for (AlgebraicOP op : geAllIntersectedOPs(ip)) {
				if (!(op instanceof ScanOP)) {
					return null;
				}
				ScanOP sp = (ScanOP) op;
				if (!(sp.getSubquery() instanceof ConstantOP)) {
					return null;
				}
				Concept cp = ((ConstantOP) sp.getSubquery()).getQueryCocnept();
				as.add(cp);
				//optimization: consider Si's concept as well. July 2013.
				if(PlanEnumeration.useMoreGeneralSelectionRemovingCondition){
					as.add(sp.getSecondaryIndex().getCi());
				}
				//
			}
			// test condition
			try {
				if (res.subsumesGuarded(Concept.getConjunctiveConcept(as), selc, kb)) {
					// System.out.println("Selection removed:"+so);
					return ip;
				}
			} catch (CoreReasoningException e) {
				e.printStackTrace();
			}
		} else if (so.getSubquery() instanceof ScanOP) {
			// using only one Si
			ScanOP sp = (ScanOP) so.getSubquery();
			if (!(sp.getSubquery() instanceof ConstantOP)) {
				return null;
			}
			Concept cons = ((ConstantOP) sp.getSubquery()).getQueryCocnept();
			as.add(cons);
			//optimization: consider Si's concept as well. July 2013.
			if(PlanEnumeration.useMoreGeneralSelectionRemovingCondition){
				as.add(sp.getSecondaryIndex().getCi());
			}
			try {
				if (res.subsumesGuarded(Concept.getConjunctiveConcept(as), selc, kb)) {
					return sp;
				}
			} catch (CoreReasoningException e) {
				e.printStackTrace();
			}
		}
		return null;
	}

	// for (AND Si(Ci))
	private static Set<AlgebraicOP> geAllIntersectedOPs(JoinOP ip) {
		Set<AlgebraicOP> ops = new HashSet<AlgebraicOP>();
		if (ip.getFirstSubQuery() instanceof JoinOP) {
			ops.addAll(geAllIntersectedOPs((JoinOP) ip.getFirstSubQuery()));
		} else {
			ops.add(ip.getFirstSubQuery());
		}
		if (ip.getSecondSubQuery() instanceof JoinOP) {
			ops.addAll(geAllIntersectedOPs((JoinOP) ip.getSecondSubQuery()));
		} else {
			ops.add(ip.getSecondSubQuery());
		}
		return ops;
	}

	@Override
	public boolean applicable() {
		if (op instanceof SelectionOP) {
			SelectionOP so = op.asSelectionOP();
			if (so.getSubquery().hasOnlyScanOP()) {
				return true;
			}
		}
		return false;
	}

}
